#!/usr/bin/perl
#######################################################################
#                        update-dnsomatic
#
# Perl script which updates DNS-O-Matic with your current IP address.
# Inspired from original code posted on the OpenDNS forum here...
# <http://forums.opendns.com/comments.php?DiscussionID=1135>
# 
# ... and hacked about by prawn: sean dot prawn at gmail dot com
# to specifically work with DNS-0-Matic and to use standard perl modules.
#
# Overview:  Program visits http://myip.dnsomatic.com/ to get your IP
#            address.  If it has changed, it will perfom a web update
#            on the DNS-O-Matic site and store your new IP address in 
#            /etc/update-dnsomatic/myip.
#
#
#######################################################################
#######################################################################
#
# Copyright 2008, prawn.  sean dot prawn at gmail dot com
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
#######################################################################


use strict;
use warnings;
use Time::localtime qw(ctime);
use File::Basename;
use LWP::UserAgent;
use Sys::Syslog;

  my ($progname, $progpath, $progsuffix) = fileparse($0, qr"\..*");
  my $version = "0.2";
  my $write_new_ip = 0;
  $| = 1;
 
  my %config;
  load_config();
   
  my ($address, $error) = get_address();
  
  if ($error) {
  
    write_log("err", "$error");
    die "Program failed: $error\n";
    
  }

  my ($update) = update_dnsomatic();

  if ($update =~ /badauth/ || $update =~ /nohost/ || 
      $update =~ /abuse/ || $update =~ /^401*/) {
 
    handle_error( $update );
    exit(0);
 
  } elsif ($update =~ /good/) {
 
    write_log("info", "Successful update of account $address");
    write_new_ip();
    print "$update\n";
    
 
  } elsif ($update =~ /nochg/) {

    write_log("info", "No change in IP Address $address nothing to update.");
 
  } else {
 
    write_log("info", 
              "Update of the account gave no known response $update");
 
  }
  
############################ Load config file #################################
sub load_config {

  #set config defaults;
  $config{host}     = 'updates.dnsomatic.com';
  $config{port}     = '443';
  $config{path}     = '/nic/update?';
  $config{myhost}   = 'all.dnsomatic.com';
  $config{wildcard} = 'NOCHG';
  $config{mx}       = 'NOCHG';
  $config{backmx}   = 'NOCHG';

  my $config_file = "/etc/update-dnsomatic/config";

  #load config
  my ($config_line, $name, $value);

  if (!open(CONFIG, "$config_file")) {
  
    write_log("err", "Config file not found : $config_file");
    exit(0);
 
  }

  while ($config_line = <CONFIG>) {

    chomp ($config_line);     
    $config_line =~ s/^\s*//;  
    $config_line =~ s/\s*$//;   

    if (($config_line !~ /^#/) && ($config_line ne "")){   

      ($name, $value) = split (/=/, $config_line);  
      $name =~ s/\s*$//;
      $name = lc($name);
      $value =~ s/^\s*//;
      $config{$name} = $value;                    

    }

  }

  close(CONFIG);
 
}

############## GET THE IP ADDRESS FROM myip.dnsomatic.com #####################
sub get_address {
  
  my $ua = LWP::UserAgent->new;

  # Define user agent type
  $ua->agent("Update-DNSOMatic/$version");
 
  my $address;
   
  my $response = $ua->get("http://myip.dnsomatic.com/"); # get IP address

  if ($response->is_success) {
  
    $address = $response->content;
    print "Your IP address is $address.\n";

  } else {

    $address = 0;
    $error = "Get IP Address error" . ' ' . $response->status_line; 

  }
  
  return ( $address, $error );

}

######################## UPDATE DNSOMATIC WITH ADDRESS ########################
sub update_dnsomatic {
 
  if (update_needed()) {
  
    my $ua = LWP::UserAgent->new;

    # Define user agent type
    $ua->agent("Update-DNSOMatic/$version");
    
    # Request URL
    my $url = "https://"   . $config{host} . $config{path} . 
              "hostname="  . $config{myhost} .
              "&myip="     . $address .
              "&mx="       . $config{mx} .
              "&backmx="   . $config{backmx} .
              "&wildcard=" . $config{wildcard}; 
  
    my $req = HTTP::Request->new(GET => $url);
 
    $req->authorization_basic($config{user}, $config{pass});

    # Make the request
    my $res = $ua->request($req);

    # Check the response
    if ($res->is_success) {
    
      my @results = $res->content;

      foreach my $s (@results) {
       
        write_log("info", "Update result: $s");
      
      }        
      
      return ($res->content);
 
    } else {

      return ($res->status_line);

    }

  } else {

    return ("nochg");

  }
  
}

############################# Handle Error messages ###########################
sub handle_error {

  my $error = shift;
 
  if ( $error =~ /badauth/ ) {

    write_log("err", "Bad Authorization attempt. HTTP reply was: $error");

  } elsif ($error =~ /nohost/) {

    write_log("err", "Bad host! the DDNS hostname you are trying to update" . 
              "does not exist.");

  } elsif ($error =~ /abuse/) {

    write_log("err", "Abuse!: OpenDNS received more than one update per minute");

  } else {

    write_log("err", "HTTP_REPLY was $error");

  }

  exit(0);

}

#################Check if the current IP is different to stored IP#############
sub update_needed {

  my $old_ip = " ";
  my $s;
  my $fn = "/etc/update-dnsomatic/myip";
  
  if (-e "$fn") {
  
    open(INF, "<$fn") || die "Cannot open $fn $!";
    chomp($s = <INF>);
    
    if ($s) {
      $s =~ s/^\s+//;
      $s =~ s/\s+$//;
      $old_ip = $s;
    }
    
    close(INF);
    
  }
  
  if ($old_ip ne $address) {

    $write_new_ip = 1;
    return(1);

  } else {

    return(0);
    
  }
  
}

############################ Store new IP Address #############################
sub write_new_ip {

  my $fn = "/etc/update-dnsomatic/myip";

  open(OUF, ">$fn") || die "Cannot open $fn $!";
  print(OUF $address . "\n");
  close(OUF);

}

###############################  Write to syslog ##############################
sub write_log {
  
  my $priority = shift;
  my $msg = shift;

  print "$msg\n"; 

  openlog($progname, 'pid,cons', 'user');
  syslog($priority, $msg);
  closelog();
  
  return 1;
    
}

exit(0);
